﻿using System;
using System.Collections;
using NUnit.Framework;

namespace SpecUnit.Samples.Atm
{
    [TestFixture]
    public class AtmSpecification : Specification
    {
        [Test, TestCaseSource("DataForWithdrawl")]
        public void AccountHasSufficientFunds(int accountBalance, int withdrawlAmount, int dispenseAmount, int newBalance)
        {
            AutomatedTellerMachine atm = null;
            Card card = null;

            var availableFunds = accountBalance + 10;

            IsSatisfiedBy(
                Scenario("Cardholder and ATM have Sufficient funds")
                    .Given("the ATM has {0}", availableFunds)
                        [() => atm = new AutomatedTellerMachine(availableFunds)]
                    .And("the account has {0}", accountBalance)
                        [() => card = new Card(true, accountBalance)]

                    .When("the account holder requests {0}", withdrawlAmount)
                        [() => atm.RequestMoney(card, withdrawlAmount)]

                    .Then("the ATM should dispense {0}", dispenseAmount)
                        [() => Assert.AreEqual(dispenseAmount, atm.DispenseValue)]
                    .And("the account balance should be {0}", newBalance)
                        [() => Assert.AreEqual(newBalance, card.AccountBalance)]

                    .AsScript);
        }

        [Test, TestCaseSource("DataForInsufficientFunds")]
        public void AccountHasInsufficientFunds(int accountBalance, int withdrawlAmount)
        {
            var atm = default(AutomatedTellerMachine);
            var card = default(Card);

            var cashInAtm = accountBalance + 100;

            IsSatisfiedBy(
                Scenario("Cardholder has insufficient funds")
                    .Given("the ATM has {0}", cashInAtm)
                        [() => atm = new AutomatedTellerMachine(cashInAtm)]
                    .And("the account has {0}", accountBalance)
                        [() => card = new Card(true, accountBalance)]

                    .When("the account holder requests {0}", withdrawlAmount)
                        [() => atm.RequestMoney(card, withdrawlAmount)]

                    .Then("the ATM should dispense $0")
                        [() => Assert.AreEqual(0, atm.DispenseValue)]
                    .And("the account balance should be unchanged")
                        [() => Assert.AreEqual(accountBalance, card.AccountBalance)]
                    .And("the ATM should display Insufficient Funds")
                        [() => Assert.AreEqual(DisplayMessage.InsufficientFunds, atm.Message)]

                    .AsScript);
        }

        public static IEnumerable DataForInsufficientFunds
        {
            get
            {
                yield return new TestCaseData(10, 20);
                yield return new TestCaseData(100, 110);
            }
        }

        public static IEnumerable DataForWithdrawl
        {
            get
            {
                yield return new TestCaseData(10, 10, 10, 0);
                yield return new TestCaseData(100, 20, 20, 80);
            }
        }
    }
}
